package org.ponly.cta4j.drawing.font;

import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.security.SecureRandom;
import java.util.*;

/**
 * 随机字体库.
 *
 * @author vacoor
 * @since 1.0
 */
public class RandomFontFamily extends AbstractFontFamily {
    /**
     * 随机生成字体的数量.
     */
    private static final int GENERATE_FONT_SIZE = 3000;

    /**
     * 字体风格.
     */
    private static final int[] STYLES = {
            Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC
    };

    /**
     * 默认忽略的字体, 这些字体容易被 OCR 识别.
     */
    private static final String[] DEFAULT_BAD_FONT_NAME_PREFIXES = {"Courier", "Times Roman"};

    /**
     * Random.
     */
    protected final Random random = new SecureRandom();

    /**
     * 生成的字体必须可以显示的字符.
     */
    private String requiredCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";

    /**
     * 同一种字体是否使用多种风格.
     */
    private boolean mixStyle = true;

    /**
     * 随机生成的字体.
     */
    private Font[] generated;

    /**
     * 创建一个默认实例.
     */
    public RandomFontFamily() {
        this(new Font[0]);
    }

    /**
     * 根据给定的字体创建实例.
     *
     * @param fonts 字体列表
     */
    public RandomFontFamily(final Font... fonts) {
        Font[] allFonts = fonts;
        if (1 > fonts.length) {
            allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        }
        this.generated = prepareAndGenerateFonts(allFonts);
    }

    /**
     * 根据给定的字号和字体创建实例.
     *
     * @param minFontSize 最小字号
     * @param maxFontSize 最大字号
     * @param fonts       字体列表
     */
    public RandomFontFamily(final int minFontSize, final int maxFontSize, final Font... fonts) {
        super(minFontSize, maxFontSize);

        Font[] allFonts = fonts;
        if (1 > fonts.length) {
            allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        }
        this.generated = prepareAndGenerateFonts(allFonts);
    }

    /**
     * 根据给定的字号和字体创建实例.
     *
     * @param minFontSize 最小字号
     * @param maxFontSize 最大字号
     * @param fonts       字体列表
     * @param mixStyle    是否允许同一种字体出现多种样式
     */
    public RandomFontFamily(final int minFontSize, final int maxFontSize, final Font[] fonts, final boolean mixStyle) {
        super(minFontSize, maxFontSize);

        Font[] allFonts = fonts;
        if (1 > fonts.length) {
            allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        }
        this.generated = prepareAndGenerateFonts(allFonts);
        this.mixStyle = mixStyle;
    }

    /**
     * 根据给定的必须字符和字体创建实例.
     *
     * @param requiredCharacters 必须字符
     * @param fonts              字体列表
     */
    public RandomFontFamily(final String requiredCharacters, final Font... fonts) {
        Font[] allFonts = fonts;
        if (1 > fonts.length) {
            allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        }
        this.requiredCharacters = requiredCharacters;
        this.generated = prepareAndGenerateFonts(allFonts);
    }

    /**
     * 预处理并生成随机字体.
     *
     * @param fonts 预处理字体
     * @return 随机生成的字体
     */
    private Font[] prepareAndGenerateFonts(final Font... fonts) {
        final List<Font> candidates = prepare(fonts);

        if (1 > candidates.size()) {
            throw new IllegalStateException("no invalid font found.");
        }

        final Font[] generated = new Font[GENERATE_FONT_SIZE];
        for (int i = 0; i < generated.length; i++) {
            final Font f = candidates.get(Math.abs(random.nextInt(candidates.size())));
            generated[i] = applyStyle(f);
        }
        return generated;
    }

    /**
     * 预处理字体, 过滤不合法字体(无法显示必须字符或设置跳过).
     *
     * @param fonts 待处理字体
     * @return 合法字体列表
     */
    private List<Font> prepare(final Font... fonts) {
        final List<Font> candidates = new ArrayList<Font>();

        Collections.addAll(candidates, fonts);
        for (Iterator<Font> it = candidates.iterator(); it.hasNext();) {
            final Font font = it.next();
            if (shouldSkip(font) || !canDisplay(font)) {
                it.remove();
            }
        }
        return candidates;
    }

    /**
     * 是否应该跳过当前字体.
     *
     * @param font 字体
     * @return 是否跳过
     */
    protected boolean shouldSkip(final Font font) {
        // TODO
        final String[] ignoreFontPrefixes = DEFAULT_BAD_FONT_NAME_PREFIXES;
        for (final String prefix : ignoreFontPrefixes) {
            if (null != prefix && !"".equals(prefix) && font.getName().startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 给定字体是否可以显示必须的字符.
     *
     * @param font 字体
     * @return 是否可以显示必须的字符
     */
    protected boolean canDisplay(final Font font) {
        // TODO
        for (int i = 0; i < requiredCharacters.length(); i++) {
            if (!font.canDisplay(requiredCharacters.charAt(0))) {
                return false;
            }
        }
        return true;
    }

    /**
     * 应用定制的字体样式.
     *
     * @param font 预处理字体
     * @return 样式处理后的字体
     */
    protected Font applyStyle(final Font font) {
        final int min = getMinFontSize();
        final int max = getMaxFontSize();
        final int delta = max - min;
        final int increment = delta > 0 ? Math.abs(random.nextInt(delta)) : 0;
        final int style = mixStyle ? STYLES[Math.abs(random.nextInt(STYLES.length))] : font.getStyle();

        return font.deriveFont(style, min + increment);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Font getFont() {
        return generated[random.nextInt(generated.length)];
    }

    /**
     * @return 同一种字体是否允许多种样式
     */
    public boolean isMixStyle() {
        return mixStyle;
    }

    /**
     * 设置同一种字体是否允许出现多种样式.
     *
     * @param mixStyle 混合样式
     */
    public void setMixStyle(final boolean mixStyle) {
        this.mixStyle = mixStyle;
    }
}
